import {Node,Prefab, instantiate } from "cc";

class ObjectPool{
    /**
     * @en The pool handler component, it could be the class name or the constructor.
     * @zh 缓冲池处理组件，用于节点的回收和复用逻辑，这个属性可以是组件类名或组件的构造函数。
     */
    public poolHandlerComp: string;
    private _pool: Node[];
    private _prefab : Prefab;

    /**
     * @en
     * Constructor for creating a pool for a specific node template (usually a prefab).
     * You can pass a component (type or name) argument for handling event for reusing and recycling node.
     * @zh
     * 使用构造函数来创建一个节点专用的对象池，您可以传递一个组件类型或名称，用于处理节点回收和复用时的事件逻辑。
     * @param poolHandlerComp @en The constructor or the class name of the component to control the unuse/reuse logic. @zh 处理节点回收和复用事件逻辑的组件类型或名称。
     * @example
     * import { NodePool, Prefab } from 'cc';
     *  properties: {
     *      template: Prefab
     *     },
     *     onLoad () {
     *       // MyTemplateHandler is a component with 'unuse' and 'reuse' to handle events when node is reused or recycled.
     *       this.myPool = new NodePool('MyTemplateHandler');
     *     }
     *  }
     */
    constructor (poolHandlerComp?:string) {
        this.poolHandlerComp = poolHandlerComp;
        this._pool = [];
    }
    
    get Pre(){return this._prefab;}
    set Pre(prefab){ this._prefab = prefab;}

    /**
     * @en The current available size in the pool
     * @zh 获取当前缓冲池的可用对象数量
     */
    public size (): number {
        return this._pool.length;
    }

    /**
     * @en Destroy all cached nodes in the pool
     * @zh 销毁对象池中缓存的所有节点
     */
    public clear (): void {
        const count = this._pool.length;
        for (let i = 0; i < count; ++i) {
            this._pool[i].destroy();
        }
        this._pool.length = 0;
    }

    /**
     * @en Put a new Node into the pool.
     * It will automatically remove the node from its parent without cleanup.
     * It will also invoke unuse method of the poolHandlerComp if exist.
     * @zh 向缓冲池中存入一个不再需要的节点对象。
     * 这个函数会自动将目标节点从父节点上移除，但是不会进行 cleanup 操作。
     * 这个函数会调用 poolHandlerComp 的 unuse 函数，如果组件和函数都存在的话。
     * @example
     * import { instantiate } from 'cc';
     * const myNode = instantiate(this.template);
     * this.myPool.put(myNode);
     */
    public put (node: Node): void {
        // indexOf 去数组中获取某个元素的索引值 如果没有 就是-1
        if (node && this._pool.indexOf(node) === -1) {
            // Remove from parent, but don't cleanup
            node.removeFromParent();
            const handler = this.poolHandlerComp ? node.getComponent(this.poolHandlerComp) : null;
            if (handler && handler['unuse']) {
                handler['unuse']();
            }

            this._pool.push(node);
        }
    }

    /**
     * @en Get a obj from pool, if no available object in pool, null will be returned.
     * This function will invoke the reuse function of poolHandlerComp if exist.
     * @zh 获取对象池中的对象，如果对象池没有可用对象，则返回空。
     * 这个函数会调用 poolHandlerComp 的 reuse 函数，如果组件和函数都存在的话。
     * @param args - 向 poolHandlerComp 中的 'reuse' 函数传递的参数
     * @example
     *   let newNode = this.myPool.get();
     */
    public get (parent?:Node,...args: any[]): Node | null {
        let last = this._pool.length - 1;
        if (last < 0) {
            // 不够就创建一个
            const node = instantiate(this._prefab);
            this.put(node);
            last++;
        } 
        
        // Pop the last object in pool
        const obj = this._pool[last];
        this._pool.length = last;
        // 把结点挂载到父节点上
        parent && (obj.parent = parent);
        // Invoke pool handler
        // @ts-ignore
        const handler = this.poolHandlerComp ? obj.getComponent(this.poolHandlerComp) : null;
        if (handler && handler['reuse']) {
            handler['reuse'](...args);
        }
        return obj;
    }
}

export class PoolManager {
    private _pools:Map<string,ObjectPool> = new Map();

    // 单例
    private static _instance:PoolManager = null;
    private constructo(){}
    static get Instance(){
        this._instance = this._instance || new PoolManager();
        return this._instance;
    }

    createPool(prefab:Prefab,count?:number|string,script?:string){
        const data = this._pools.get(prefab.name);
        if (data) {
            return;
        }
        // 默认个数100
        let len:number = 100;
        // 脚本名称
        let ts:string = '';
        // 如果第三个参数存在 那么它就是脚本名称
        if (script) {
            ts = script; 
        }
        // 第三个参数不存在  
        // count是number类型 说明它是节点个数
        else if (typeof(count) === 'string') {
            ts = count;
        }
        // 不管第三个参数是否存在  只要是number 就是节点个数
        if (typeof(count) === 'number') {
            len = count;
        }
        
        // ts座位对象池的构造函数的参数
        const pool = new ObjectPool(ts);
        for (let i = 0; i < len; i++) {
            const node = instantiate(prefab);
            pool.put(node);
        }
        // 把预制体绑定到池的对象上
        pool.Pre = prefab;
        // 把对象池 存储到管理者容器中
        this._pools.set(prefab.name,pool);
    }

    getNode(name:string,parent:Node,...args):Node{
        const pool = this._pools.get(name);
        if (pool) {
            const node = pool.get(parent,...args);
            return node;
        }
        return null;
    }
    // 节点回收
    putNode(node:Node){
        const pool = this._pools.get(node.name);
        if (pool) {
            pool.put(node);
        }
    }

    removePool(name:string){
        const pool = this._pools.get(name);
        if (!pool) {
            return;
        }
        // 池子里的节点 全部销毁
        pool.clear();
        this._pools.delete(name);
    }

    clear(){
        this._pools.forEach(pool => {
            pool.clear();
        });
        this._pools.clear();
    }
}